package com.xiyuan;

import com.xiyuan.smartutils.Files;
import com.xiyuan.smartutils.Lists;
import com.xiyuan.smartutils.Resources;
import com.xiyuan.smartutils.Systems;
import com.xiyuan.smartutils.constants.SignConstants;
import com.xiyuan.smartutils.constants.SysConstants;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;


/**
 * 系统工程类路径，默认取当前目录下的lib目录
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */
public final class SysClasspath implements SysConstants, SignConstants {
    private List<String> classpath_list = new ArrayList<>();
    private ClassLoader parentLoader = null;
    private BootLoader classLoader = null;
    
    /**
     * 获取全局ClassLoader
     */
    public ClassLoader getClassLoader() {
        return this.classLoader;
    }
    
    /**
     * 初始化CLASSPATH
     */
    public void initClasspath(String libPaths) throws IOException {
        classpath_list.clear();
        
        HashSet<String> alreadys = new HashSet<>();
        
        // 1.加入lib目录
        File libFolder = new File(LIB_FOLDER);
        addJarFolder(LIB_FOLDER, libFolder);
        alreadys.add(libFolder.getCanonicalPath());
        
        // 2.加入配置的boot.lib目录下*.jar和*.zip
        List<String> libPathList = Lists.toStringList(libPaths);
        for (String libPath : libPathList) {
            File libDir = new File(libPath);
            if (!Files.isDirectory(libDir)) {continue;}
            
            String canonicalPath = libDir.getCanonicalPath();
            if (alreadys.contains(canonicalPath)) {
                continue;// 排重
            }
            
            addJarFolder(libPath, libDir);
            alreadys.add(canonicalPath);
        }
        
        // 3.设置ClassPath和ClassLoader
        Systems.setClassPath(toString());
        classLoader = getClassLoaderNew();
        Thread.currentThread().setContextClassLoader(classLoader);
    }
    
    public void releaseClasspath() {
        Thread.currentThread().setContextClassLoader(parentLoader);
        try {
            classLoader.close();
        }
        catch (IOException e) {
        }
    }
    
    /**
     * 加载目录下jar,zip到列表
     */
    private void addJarFolder(String libPath, File libDir) throws IOException {
        // 2.1把目录下*.jar和*.zip加入到classpath中
        File[] jarFileArr = libDir.listFiles(new FileFilter() {
            public boolean accept(File dir) {
                String name = dir.getName().toLowerCase();
                return name.endsWith(".jar") || name.endsWith(".zip");
            }
        });
        
        for (int i = 0; i < jarFileArr.length; i++) {addJarFile(jarFileArr[i]);}
        
        // 2.2把目录加到虚拟机动态库目录，方便JNI调用的.dll,.so等库
        Systems.addLibraryPath(libPath);
    }
    
    /**
     * 加入JAR文件到列表
     */
    private boolean addJarFile(File jar) throws IOException {
        if (jar == null || !jar.exists()) {return false;}
        
        String key = jar.getCanonicalPath();
        if (!classpath_list.contains(key)) {
            classpath_list.add(key);
            return true;
        }
        
        return false;
    }
    
    /**
     * 创建新的类加载器
     */
    private BootLoader getClassLoaderNew() throws MalformedURLException {
        int size = classpath_list.size();
        URL[] urls = new URL[size];
        for (int i = 0; i < size; i++) {urls[i] = new File(classpath_list.get(i)).toURI().toURL();}
        
        parentLoader = Thread.currentThread().getContextClassLoader();
        if (parentLoader == null) {parentLoader = SysClasspath.class.getClassLoader();}
        
        if (parentLoader == null) {parentLoader = ClassLoader.getSystemClassLoader();}
        
        return new BootLoader(urls, parentLoader);
    }
    
    public String toString() {
        int size = classpath_list.size();
        if (size == 0) {return _EMPTY_;}
        
        StringBuilder strb = new StringBuilder(1024);
        // 第一个
        strb.append(classpath_list.get(0));
        // 余下的
        for (int i = 1; i < size; i++) {strb.append(File.pathSeparatorChar).append(classpath_list.get(i));}
        
        return strb.toString();
    }
    
    public List<String> getClasspathList() {
        return new ArrayList<>(classpath_list);
    }
    
    /**
     * 定义一个引导加载器
     */
    private class BootLoader extends URLClassLoader {
        private String name = "lgz8669-ClassLoader";
        
        private BootLoader(URL[] urls, ClassLoader parent) {
            super(urls, parent);
        }
        
        public String toString() {
            return name;
        }
        
        @Override
        public URL getResource(String name) {
            int javaVersion = Systems.JAVA_MAIN_VERSION;
            for (int version = javaVersion; version >= 8; version--) {
                String versionedPath = "META-INF/versions/" + version + "/" + name;
                if (Resources.exists(versionedPath)) {
                    return super.getResource(versionedPath);
                }
                URL url = super.getResource(versionedPath);
                if (url != null) {
                    return url;
                }
            }
            return super.getResource(name);
        }
        
        private byte[] readBytes(InputStream is) throws IOException {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] data = new byte[4096];
            int bytesRead;
            try {
                while ((bytesRead = is.read(data, 0, data.length)) != -1) {
                    buffer.write(data, 0, bytesRead);
                }
                buffer.flush();
                return buffer.toByteArray();
            } finally {
                is.close(); // 手动关闭流
            }
        }
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            // 1. 获取当前运行环境的 JDK 版本
            int javaVersion = Systems.JAVA_MAIN_VERSION;
            
            // 2. 从高到低尝试匹配版本目录（例如 11 -> 9 -> 8）
            for (int version = javaVersion; version >= 8; version--) {
                String path = "META-INF/versions/" + version + "/" + name.replace('.', '/') + ".class";
                try {
                    // 3. 从 JAR 中读取类字节码
                    InputStream is = getResourceAsStream(path);
                    if (is != null) {
                        byte[] bytes = readBytes(is);
                        return defineClass(name, bytes, 0, bytes.length);
                    }
                }
                catch (IOException e) {
                    // 忽略并尝试下一个版本
                }
            }
            
            // 4. 回退到主版本类
            return super.findClass(name);
        }
    }
}
